<?php
/**
 * Created by sublime 3.
 * Auth: Inhere
 * Date: 14-10-1
 * Time: 10:35
 * Uesd: 主要功能是 安全加密
 *  uniqid(string $prefix  = "" , bool $more_entropy  = false );
 *  prefix为空，则返回的字符串长度为13。more_entropy 为 TRUE ，则返回的字符串长度为23。
 *  crypt(str,salt);
 * //mcrypt支持的加密算法列表
 * $cipher_list = mcrypt_list_algorithms();
 * //mcrypt支持的加密模式列表
 * $mode_list = mcrypt_list_modes();
 */
namespace ulue\parts\security;

useuluee\parts\security\helper\SecurityHelper;

class Cipher
{
    private $require_extensions = ['mcrypt'];
    private $_securekey, $_iv;
    protected $clearkey;
    private $_mcryptCipher      = MCRYPT_RIJNDAEL_256;
    private $_mcryptMode        = MCRYPT_MODE_ECB;

    static public $cipherList   = [];
    static public $modeList     = [];

    /**
     * [__construct description]
     * @param [type] $clearkey [明文 key]
     */
    public function __construct($clearkey)
    {
      $this->_checkExtendsIsLoaded( $this->require_extensions );
      $this->init();

    }

    private function init($clearkey)
    {
        if (empty($clearkey) || !is_string($clearkey))
        {
            throw new \DDataTypeException("请传入不为空的字符串参数作为加密秘钥！");
        }

        $this->clearkey   = trim($clearkey);
        $this->_securekey = hash('sha256',$clearkey,TRUE);
        $this->_iv        = mcrypt_create_iv(32);
    }

    public function setSecureKey($value)
    {
        if (empty($clearkey) || !is_string($clearkey))
        {
            throw new \DDataTypeException("请传入不为空的字符串参数！");
        }

      $this->_securekey = $value;
    }

    public function setIV($value)
    {
        if (empty($clearkey) || !is_string($clearkey))
        {
            throw new \DDataTypeException("请传入不为空的字符串参数！");
        }

        $this->_iv        = $value;
    }

    public function enc($input)
    {
        $this->_checkParamIV();
        $mode = $this->_mcryptMode;

        return base64_encode(mcrypt_encrypt($this->_mcryptCipher,
                                            $this->_securekey,
                                            $input,
                                            $mode ,
                                            $this->_iv
                                        ));
    }

    public function dec($encrypted)
    {
        $mode = $this->_mcryptMode;

        return trim(mcrypt_decrypt($this->_mcryptCipher,
                                    $this->_securekey,
                                    base64_decode($encrypted),
                                    $mode ,
                                    $this->_iv
                                ));
    }

    /**
     * [enc description]
     * @param  [type] $string [description]
     * @param  [type] $clearkey    [description]
     * @return [type]         [description]
     */
    public function encode($string,$clearkey)
    {
        $this->_checkParamIV();
        $securekey   = $this->_securekey;
        $mode        = $this->_mcryptMode;

        # 自带的加密函数,得到加密后的字符串
        $cryptedText = base64_encode(mcrypt_encrypt($this->_mcryptCipher,
                                                    $securekey,
                                                    $string,
                                                    MCRYPT_MODE_CBC,
                                                    md5(md5($clearkey))
                                                ));
        # 对特殊字符进行处理
        $encrypted   = trim(self::_safeB64encode($cryptedText));

        return $encrypted;
    }

    public function decode($encrypted,$clearkey)
    {
        $securekey   = $this->_securekey;
        $mode        = $this->_mcryptMode;

        # 对特殊字符解析，得到加密后的字符串
        $cryptedText = self::_safeB64decode($encrypted);

        # 解密函数
        $decryptedtb = rtrim(mcrypt_decrypt($this->_mcryptCipher,
                                            $securekey,
                                            base64_decode($crypttexttb),
                                            MCRYPT_MODE_CBC,
                                            md5(md5($clearkey))
                                          ), "\0");

        return $decryptedtb;
    }


    //处理特殊字符
    static private function _safeB64encode($string)
    {
        $data = base64_encode($string);
        $data = str_replace(array('+','/','='),array('-','_',''),$data);

        return $data;
    }

    //解析特殊字符
    static private function _safeB64decode($string)
    {
        $data = str_replace(array('-','_'),array('+','/'),$string);
        $mod4 = strlen($data) % 4;

        if ($mod4) {
            $data .= substr('====', $mod4);
        }

        return base64_decode($data);
    }

    private function _checkExtendsIsLoaded($extensions)
    {
        $re = SecurityHelper::extendsIsLoaded($extensions);

        if (!$re) {
            \Trigger::error(implode(',', $extensions).'extension is required !','sys');
        }
    }

    // CBC, CFB, OFB 模式中用于初始化过程，在 STREAM 模式中用于某些算法，就必须有 iv
    private function _checkParamIV()
    {
        $mode = trim(strrchr($this->_mcryptMode,'_') ,'_ ' );

        if ($this->_iv == '' && in_array($this->_mcryptMode, ['cbc','cfb','ofb','stream']) )
        {
            throw new \DMissArgumentException("缺少必要的参数！");
        }
    }

    /**
     * setCipher 设置算法类型
     */
    public function setCipher($cipher)
    {
        $cipher     = trim($cipher);
        $cipherList = $this->getCipherList();

        if ( in_array($cipher, $cipherList) )
        {
            $this->_mcryptCipher = $cipher;
        }
    }

    public function getCipher()
    {
        return $this->_mcryptCipher;
    }

    public function getCipherList()
    {
        // $cipherList = [
        //   'cast-128', 'gost', 'rijndael-128', 'twofish',
        //   'cast-256', 'loki97', 'rijndael-192', 'saferplus',
        //   'wake', 'blowfish-compat', 'des', 'rijndael-256',
        //   'serpent', 'xtea', 'blowfish', 'enigma', 'rc2', 'tripledes', 'arcfour'
        // ];
        if (empty(self::$cipherList))
        {
            self::$cipherList = mcrypt_list_algorithms();
        }

        return self::$cipherList;
    }
    /**
     * [setMode 设置算法使用模式]
     * @param [type] $mode [description]
     */
    public function setMode($mode)
    {
        $mode     = trim($mode);
        $modeList = $this->getModeList();

        if ( in_array($mode, $modeList) )
        {
            $this->_mcryptMode = $mode;
        }
    }

    public function getMode()
    {
        return $this->_mcryptMode;
    }

    public function getModeList()
    {
        //[ 'ecb', 'cbc', 'cfb', 'ofb', 'nofb', 'stream', ];
        if (empty(self::$modeList))
        {
            self::$modeList = mcrypt_list_modes();
        }

        return self::$modeList;
    }


}